﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace HotelResSystem2
{
    public class DataStorage
    {
        private const string RESERVATION_XML = "/Reservations.xml";
        private static DataStorage dataStorage_instance = new DataStorage();
        private XmlDocument xmlDoc;
        private XmlReader reader;

        private DataStorage()
        {
            // Private constructor for singleton instance
        }

        public static DataStorage GetInstance()
        {
            return dataStorage_instance;
        }
        
        private void openReader(string filename)
        {
            reader = XmlReader.Create(filename);
        }

        private void closeReader()
        {
            reader.Close();
        }

        private void openWriter(string filename)
        {
            // Open the writer with the existing XML data
            xmlDoc = new XmlDocument();
            xmlDoc.Load(filename);
        }

        private void closeWriter(string filename)
        {
            xmlDoc.Save(filename);
        }

        public void saveReservationInfo(Reservation res)
        {
            openWriter(Properties.Settings.Default.XMLFolder + RESERVATION_XML);

            XmlNode reservation = createReservationElement(res);

            // Append the new reservation element to the refGuest element and close the writer
            xmlDoc.GetElementsByTagName("Reservations")[0].AppendChild(reservation);
            closeWriter(Properties.Settings.Default.XMLFolder + RESERVATION_XML);
        }

        /// <summary>
        /// This method updates the reservation information in the XML data source.
        /// It is effectively the "Check-in" method with more complete information.
        /// </summary>
        /// <param name="res">The reservation information to be updated.</param>
        public void updateReservationInfo(Reservation res)
        {
            bool nodeUpdate = false;
            openWriter(Properties.Settings.Default.XMLFolder + RESERVATION_XML);
            XmlNodeList reservationNodes = xmlDoc.GetElementsByTagName("Reservation");
            foreach (XmlNode reservationNode in reservationNodes)
            {
                foreach (XmlNode childNode in reservationNode.ChildNodes)
                {
                    if (childNode.Name == "ReservationNumber" && childNode.InnerText == res.ResID)
                    {
                        // Remove the old node and create and add the new one
                        XmlNode parentNode = reservationNode.ParentNode;
                        parentNode.RemoveChild(reservationNode);
                        XmlNode newReservation = createReservationElement(res);
                        parentNode.AppendChild(newReservation);
                        nodeUpdate = true;
                        break;
                    }
                }
                if (nodeUpdate)
                {
                    break;
                }
            }

            closeWriter(Properties.Settings.Default.XMLFolder + RESERVATION_XML);
        }

        public void removeReservationInfo(Reservation res)
        {
            bool nodeRemoved = false;
            
            // Open the reservation xml file and search for the reservation to remove
            openWriter(Properties.Settings.Default.XMLFolder + RESERVATION_XML);
            XmlNodeList reservationNodes = xmlDoc.GetElementsByTagName("Reservation");
            foreach (XmlNode reservationNode in reservationNodes)
            {
                foreach (XmlNode childNode in reservationNode.ChildNodes)
                {
                    if (childNode.Name == "ReservationNumber" && childNode.InnerText == res.ResID)
                    {
                        reservationNode.ParentNode.RemoveChild(reservationNode);
                        nodeRemoved = true;
                        break;
                    }
                }
                if (nodeRemoved)
                {
                    break;
                }
            }

            // Close the reservation xml file
            closeWriter(Properties.Settings.Default.XMLFolder + RESERVATION_XML);
        }

        public XmlNode createReservationElement(Reservation res)
        {
            XmlElement reservation = xmlDoc.CreateElement("Reservation");

            XmlElement reservationNumber = xmlDoc.CreateElement("ReservationNumber");
            reservationNumber.InnerText = res.ResID;
            reservation.AppendChild(reservationNumber);

            XmlElement checkin = xmlDoc.CreateElement("CheckIn");
            checkin.InnerText = res.CheckInDate.ToString();
            reservation.AppendChild(checkin);

            XmlElement checkout = xmlDoc.CreateElement("CheckOut");
            checkout.InnerText = res.CheckOutDate.ToString();
            reservation.AppendChild(checkout);

            XmlElement roomNumber = xmlDoc.CreateElement("RoomNumber");
            roomNumber.InnerText = res.RoomNumber.ToString();
            reservation.AppendChild(roomNumber);

            XmlElement rateType = xmlDoc.CreateElement("RateType");
            rateType.InnerText = res.RateType != null ? res.RateType.Name : "";
            reservation.AppendChild(rateType);

            XmlElement numberOfDays = xmlDoc.CreateElement("NumberOfDays");
            numberOfDays.InnerText = res.NumberOfDays.ToString();
            reservation.AppendChild(numberOfDays);

            XmlElement numberOfAdults = xmlDoc.CreateElement("NumberOfAdults");
            numberOfAdults.InnerText = res.NumberOfAdults.ToString();
            reservation.AppendChild(numberOfAdults);

            XmlElement numberOfChildren = xmlDoc.CreateElement("NumberOfChildren");
            numberOfChildren.InnerText = res.NumberOfChildren.ToString();
            reservation.AppendChild(numberOfChildren);

            XmlElement nightlyRate = xmlDoc.CreateElement("NightlyRate");
            nightlyRate.InnerText = res.NightlyRate.ToString();
            reservation.AppendChild(nightlyRate);

            XmlElement discount = xmlDoc.CreateElement("Discount");
            discount.InnerText = res.Discount != null ? res.Discount.Name : "";
            reservation.AppendChild(discount);

            // Create the guest element
            XmlElement guest = xmlDoc.CreateElement("Guest");
            XmlElement lastName = xmlDoc.CreateElement("LastName");
            lastName.InnerText = res.PrimaryGuest.ContactInfo.LastName;
            guest.AppendChild(lastName);

            XmlElement firstName = xmlDoc.CreateElement("FirstName");
            firstName.InnerText = res.PrimaryGuest.ContactInfo.FirstName;
            guest.AppendChild(firstName);

            XmlElement addr = xmlDoc.CreateElement("Address");
            addr.InnerText = res.PrimaryGuest.ContactInfo.Address;
            guest.AppendChild(addr);

            XmlElement city = xmlDoc.CreateElement("City");
            city.InnerText = res.PrimaryGuest.ContactInfo.City;
            guest.AppendChild(city);

            XmlElement state = xmlDoc.CreateElement("State");
            state.InnerText = res.PrimaryGuest.ContactInfo.State;
            guest.AppendChild(state);

            XmlElement zip = xmlDoc.CreateElement("Zip");
            zip.InnerText = res.PrimaryGuest.ContactInfo.ZIP.ToString();
            guest.AppendChild(zip);

            reservation.AppendChild(guest);
            return reservation;
        }

        public Reservation[] GetReservationList()
        {
            List<Reservation> reservations = new List<Reservation>();
            openReader(Properties.Settings.Default.XMLFolder + RESERVATION_XML);

            while (reader.Read())
            {
                // Only detect start elements.
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Reservation")
                    {
                        Reservation reservation = new Reservation();

                        // Read the reservation info
                        reader.Read();
                        reservation.ResID = reader.ReadElementString("ReservationNumber");
                        string checkinString = reader.ReadElementString("CheckIn");
                        string checkOutString = reader.ReadElementString("CheckOut");
                        string roomNumber = reader.ReadElementString("RoomNumber");
                        string rateType = reader.ReadElementString("RateType");
                        string numberOfDays = reader.ReadElementString("NumberOfDays");
                        string numberOfAdults = reader.ReadElementString("NumberOfAdults");
                        string numberOfChildren = reader.ReadElementString("NumberOfChildren");
                        string nightlyRate = reader.ReadElementString("NightlyRate");
                        string discount = reader.ReadElementString("Discount");

                        // Read the guest info
                        reader.Read();
                        if (reader.Name == "Guest")
                        {
                            reader.Read();
                            reservation.PrimaryGuest.ContactInfo.LastName = reader.ReadElementString("LastName");
                            reservation.PrimaryGuest.ContactInfo.FirstName = reader.ReadElementString("FirstName");
                            reservation.PrimaryGuest.ContactInfo.Address = reader.ReadElementString("Address");
                            reservation.PrimaryGuest.ContactInfo.City = reader.ReadElementString("City");
                            reservation.PrimaryGuest.ContactInfo.State = reader.ReadElementString("State");
                            int zip = 0;
                            if (int.TryParse(reader.ReadElementString("Zip"), out zip))
                            {
                                reservation.PrimaryGuest.ContactInfo.ZIP = zip;
                            }
                        }

                        // add the new reservation info
                        reservation.CheckInDate = DateTime.Parse(checkinString);
                        reservation.CheckOutDate = DateTime.Parse(checkOutString);
                        reservation.RoomNumber = int.Parse(roomNumber);
                        reservation.NumberOfDays = int.Parse(numberOfDays);
                        reservation.NumberOfAdults = int.Parse(numberOfAdults);
                        reservation.NumberOfChildren = int.Parse(numberOfChildren);
                        reservation.NightlyRate = double.Parse(nightlyRate);
                        switch (rateType)
                        {
                            case RoomRateStudio.XML_TAG:
                                reservation.RateType = new RoomRateStudio();
                                break;
                            case RoomRateQueen.XML_TAG:
                                reservation.RateType = new RoomRateQueen();
                                break;
                            case RoomRateKing.XML_TAG:
                                reservation.RateType = new RoomRateKing();
                                break;
                            case RoomRateSuite.XML_TAG:
                                reservation.RateType = new RoomRateSuite();
                                break;
                            default:
                                reservation.RateType = null;
                                break;
                        }

                        switch (discount)
                        {
                            case DiscountAAA.XML_TAG:
                                reservation.Discount = new DiscountAAA();
                                break;
                            case DiscountAARP.XML_TAG:
                                reservation.Discount = new DiscountAARP();
                                break;
                            case DiscountComp.XML_TAG:
                                reservation.Discount = new DiscountComp();
                                break;
                            case DiscountLastMinute.XML_TAG:
                                reservation.Discount = new DiscountLastMinute();
                                break;
                            case DiscountRackRate.XML_TAG:
                                reservation.Discount = new DiscountRackRate();
                                break;
                        }

                        reservations.Add(reservation);
                    }
                }
            }

            closeReader();
            return reservations.ToArray();
        }

        public bool getReservationInfo(string resNum, out Reservation res)
        {
            Reservation tempReservation = new Reservation();

            bool found = false;

            this.openReader(Properties.Settings.Default.XMLFolder + RESERVATION_XML);
            
            while (reader.Read())
            {
                // Only detect start elements.
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Reservation")
                    {
                        // Read the reservation info
                        reader.Read();
                        tempReservation.ResID = reader.ReadElementString("ReservationNumber");
                        string checkinString = reader.ReadElementString("CheckIn");
                        string checkOutString = reader.ReadElementString("CheckOut");
                        string roomNumber = reader.ReadElementString("RoomNumber");
                        string rateType = reader.ReadElementString("RateType");
                        string numberOfDays = reader.ReadElementString("NumberOfDays");
                        string numberOfAdults = reader.ReadElementString("NumberOfAdults");
                        string numberOfChildren = reader.ReadElementString("NumberOfChildren");
                        string nightlyRate = reader.ReadElementString("NightlyRate");
                        string discount = reader.ReadElementString("Discount");

                        // Read the guest info
                        reader.Read();
                        if (reader.Name == "Guest")
                        {
                            reader.Read();
                            tempReservation.PrimaryGuest.ContactInfo.LastName = reader.ReadElementString("LastName");
                            tempReservation.PrimaryGuest.ContactInfo.FirstName = reader.ReadElementString("FirstName");
                            tempReservation.PrimaryGuest.ContactInfo.Address = reader.ReadElementString("Address");
                            tempReservation.PrimaryGuest.ContactInfo.City = reader.ReadElementString("City");
                            tempReservation.PrimaryGuest.ContactInfo.State = reader.ReadElementString("State");
                            int zip = 0;
                            if (int.TryParse(reader.ReadElementString("Zip"), out zip))
                            {
                                tempReservation.PrimaryGuest.ContactInfo.ZIP = zip;
                            }
                        }

                        // add the new reservation info
                        tempReservation.CheckInDate = DateTime.Parse(checkinString);
                        tempReservation.CheckOutDate = DateTime.Parse(checkOutString);
                        tempReservation.RoomNumber = int.Parse(roomNumber);
                        tempReservation.NumberOfDays = int.Parse(numberOfDays);
                        tempReservation.NumberOfAdults = int.Parse(numberOfAdults);
                        tempReservation.NumberOfChildren = int.Parse(numberOfChildren);
                        tempReservation.NightlyRate = double.Parse(nightlyRate);
                        switch (rateType)
                        {
                            case RoomRateStudio.XML_TAG:
                                tempReservation.RateType = new RoomRateStudio();
                                break;
                            case RoomRateQueen.XML_TAG:
                                tempReservation.RateType = new RoomRateQueen();
                                break;
                            case RoomRateKing.XML_TAG:
                                tempReservation.RateType = new RoomRateKing();
                                break;
                            case RoomRateSuite.XML_TAG:
                                tempReservation.RateType = new RoomRateSuite();
                                break;
                            default:
                                tempReservation.RateType = null;
                                break;
                        }

                        switch (discount)
                        {
                            case DiscountAAA.XML_TAG:
                                tempReservation.Discount = new DiscountAAA();
                                break;
                            case DiscountAARP.XML_TAG:
                                tempReservation.Discount = new DiscountAARP();
                                break;
                            case DiscountComp.XML_TAG:
                                tempReservation.Discount = new DiscountComp();
                                break;
                            case DiscountLastMinute.XML_TAG:
                                tempReservation.Discount = new DiscountLastMinute();
                                break;
                            case DiscountRackRate.XML_TAG:
                                tempReservation.Discount = new DiscountRackRate();
                                break;
                        }

                        found = true;
                        break;
                    }
                }
            }

            res = tempReservation;
            closeReader();
            return found;
        }

        public string getClassName()
        {
            this.openReader(Properties.Settings.Default.XMLFolder+ "/CreditData.xml");
            
            string className = "";

            while (reader.Read())
            {
                // Only detect start elements.
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Adapter")
                    {
                        reader.Read();
                        className = reader.ReadElementString("Name");
                    }
                }

            }

            this.closeReader();

            return className;
        }

        public List<int> getRooms()
        {
            this.openReader(Properties.Settings.Default.XMLFolder +"/RoomInv.xml");

            int tempRoomNum = 0;
            List<int> tempRooms = new List<int>();

            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Room")
                    {
                        reader.Read();
                        tempRoomNum = Convert.ToInt32(reader.ReadElementString("RoomNum"));
                        tempRooms.Add(tempRoomNum);
                    }
                }
            }

            this.closeReader();

            return tempRooms;
        }

        public Room getRoom(int roomNum)
        {
            Room tempRoom = new Room();

            this.openReader(Properties.Settings.Default.XMLFolder + "/RoomInv.xml");

            int tempNum = 0;

            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    if (reader.Name == "Room")
                    {
                        reader.Read();
                        tempNum = Convert.ToInt32(reader.ReadElementString("RoomNum"));
                        if (tempNum == roomNum)
                        {
                            tempRoom.RoomNum = tempNum;
                            tempRoom.RoomType = reader.ReadElementString("RoomType");
                            break;
                        }
                    }
                }
            }

            this.closeReader();

            return tempRoom;
        }
    }
}
